import { ConstantEnum } from '@libs/common/enums';
import {
  BaseRepository,
  indexPaginateBuilder,
  showBuilderEntity,
} from '@libs/common/repository';
import { ApiQueryHandler } from '@libs/common/utils';
import { forwardRef, Inject, Injectable } from '@nestjs/common';
import { Dayjs } from '@shencom/utils-date';
import { plainToInstance } from 'class-transformer';
import { DataSource, Equal, In } from 'typeorm';

import { DeliverService } from '@/atm/deliver/deliver.service';
import { DeviceCargoLaneService } from '@/atm/device-cargo-lane/device-cargo-lane.service';
import { DeviceExchangeLogService } from '@/atm/device-exchange-log/device-exchange-log.service';

import { OrderResult, PointTypeEnum } from '../../constant.enum';
import { PointsHistoryService } from '../points-history/points-history.service';
import { ProductService } from '../product/product.service';
import {
  CheckOrderStatusManual,
  CreateOrderDto,
  DeleteOrderDto,
  FindOrderDto,
  IndexOrderDto,
  RefundOrderDto,
  ShowOrderDto,
  UpdateOrderDto,
} from './dto/order.dto';
import { Order } from './entities/order.entity';

@Injectable()
export class OrderService extends BaseRepository {
  constructor(
    @Inject(ConstantEnum.DB_DATA_SOURCE_MANAGER) dataSource: DataSource,
  ) {
    super(dataSource);
  }

  protected repositoryManager = () => this.getRepository(Order);

  protected repository = this.getRepository(Order);

  @Inject(DeliverService)
  private readonly deliverService: DeliverService;

  @Inject(DeviceCargoLaneService)
  private readonly deviceCargoLaneService: DeviceCargoLaneService;

  @Inject(PointsHistoryService)
  private readonly pointsHistoryService: PointsHistoryService;

  @Inject(DeviceExchangeLogService)
  private readonly deviceExchangeLogService: DeviceExchangeLogService;

  @Inject(forwardRef(() => ProductService))
  private readonly productService: ProductService;

  async index(dto: IndexOrderDto) {
    const res = await indexPaginateBuilder({
      dto,
      repository: this.repository,
    });
    return res;
  }

  show<D extends ShowOrderDto = ShowOrderDto>(dto: D) {
    return showBuilderEntity(this.repository, dto);
  }

  async delete(dto: DeleteOrderDto) {
    const { id, ids } = dto;
    if (id) {
      await this.repositoryManager().delete({ id: Equal(id) });
    } else if (ids) {
      await this.repositoryManager().delete({ id: In(ids) });
    }
    return;
  }

  update(dto: UpdateOrderDto | UpdateOrderDto[]) {
    const dtos = Array.isArray(dto) ? dto : [dto];
    const entities = plainToInstance(Order, dtos);

    return this.repositoryManager().save(entities);
  }

  async create(dto: CreateOrderDto | CreateOrderDto[]) {
    const entity = this.repositoryManager().create(plainToInstance(Order, dto));
    await this.repositoryManager().insert(entity);
    return entity;
  }

  updateStatus(id: string, status: CreateOrderDto['status']) {
    return this.repositoryManager().update(id, { status });
  }

  findUser(dto: IndexOrderDto, userId: string) {
    const query = ApiQueryHandler(userId, 'userId', 'EQ');

    dto.query = query;
    return this.index(dto);
  }

  find(dto?: FindOrderDto) {
    return this.repository.find({
      where: dto || {},
    });
  }

  async createOrder(dto: CreateOrderDto) {
    // 异常的情况也要创建记录
    const entity = this.repository.create(
      plainToInstance(Order, {
        userId: dto.userId,
        userName: dto.userName,
        orderNo: dto.orderNo,
        totalPrice: dto.totalPrice,
        status: dto.status,
        deviceTransNo: dto.deviceTransNo,
        deviceRoadId: dto.deviceRoadId,
        deviceRoadNo: dto.deviceRoadNo,
        productId: dto.productId,
        productName: dto.productName,
        productPrice: dto.productPrice,
        productNumber: dto.productNumber,
        productImage: dto.productImage,
      }),
    );
    await this.repository.insert(entity);

    return entity;
  }

  async checkOrderStatus(mcuid: string, order: Order) {
    try {
      // 检查设备出货状态
      const atmResult = await this.deliverService.checkOrderResult(
        order.deviceTransNo,
        mcuid,
      );

      if (!atmResult) {
        this.deviceExchangeLogService.updateStatusByOrderNo({
          orderNo: order.orderNo,
          status: OrderResult['失败'],
        });
        throw new Error('设备检查异常');
      }

      if (atmResult.result === OrderResult['待出货']) {
        this.deviceExchangeLogService.updateStatusByOrderNo({
          orderNo: order.orderNo,
          status: atmResult.result,
        });
        return { status: atmResult.result };
      }

      if (
        atmResult.result === OrderResult['成功'] ||
        atmResult.result === OrderResult['失败']
      ) {
        let pointsHistoryId: string | null = null;

        if (atmResult.result === OrderResult['成功']) {
          const { deviceRoadId, productId } = order;

          const cargoLane = await this.deviceCargoLaneService.findOne(mcuid, {
            deviceRoad: deviceRoadId,
          });

          if (!cargoLane) return;

          // 更新货道信息
          await this.deviceCargoLaneService.update(mcuid, {
            id: cargoLane.id,
            productId: productId,
            store: cargoLane.store! - order.productNumber,
          });

          // 记录积分变动历史
          const pointsHistory = await this.pointsHistoryService.create({
            userId: order.userId,
            userName: order.userName,
            orderNo: order.orderNo,
            points: order.totalPrice,
            type: PointTypeEnum['消费'],
            productName: order.productName,
            productNumber: order.productNumber,

            desc: `类型：${PointTypeEnum[PointTypeEnum['消费']]}，商品：${
              order.productName
            }，数量：${order.productNumber}，总价：${order.totalPrice}积分`,
          });

          pointsHistoryId = pointsHistory.id;
        }

        // 更新订单状态
        this.updateStatus(order.id, atmResult.result);

        this.deviceExchangeLogService.updateStatusByOrderNo({
          orderNo: order.orderNo,
          status: atmResult.result,
        });

        return { status: atmResult.result, pointsHistoryId };
      }
    } catch (error) {
      throw error;
    }
  }

  /** 手动检查订单状态 */
  async checkOrderStatusManual(dto: CheckOrderStatusManual) {
    const order = await this.repository.findOneBy({
      id: dto.id,
      status: In([0, -1]),
    });

    if (!order) throw new Error('订单不存在');

    const { deviceRoadId, deviceTransNo } = order;
    // 通过设备货道ID查询设备 mcuid
    const device =
      await this.deviceCargoLaneService.findIdByMcuid(deviceRoadId);

    const mcuid = device?.deviceRoad?.mcuid;

    if (!mcuid) throw new Error('设备不存在');

    const atmResult = await this.deliverService.checkOrderResult(
      deviceTransNo,
      mcuid,
    );

    if (!atmResult) throw new Error('未找到兑换记录');

    // 改变订单状态
    await this.updateStatus(order.id, atmResult.result);

    // 扣除积分
    await this.pointsHistoryService.create({
      userId: order.userId,
      userName: order.userName,
      orderNo: order.orderNo,
      points: order.totalPrice,
      type: PointTypeEnum['消费'],
      productName: order.productName,
      productNumber: order.productNumber,

      desc: `类型：${PointTypeEnum[PointTypeEnum['消费']]}，商品：${
        order.productName
      }，数量：${order.productNumber}，总价：${order.totalPrice}积分`,
    });
  }

  // // 定时检查订单状态
  // @Cron('0 0 01 * * *')
  // private async timingCheckOrderStatus() {
  //   const orders = await this.repositoryManager().find({
  //     where: { status: OrderResult['待出货'] },
  //     // 后期加个时间限制
  //   });

  //   if (!orders.length) return;

  //   for (const order of orders) {
  //     await this.checkOrderStatus(order);
  //   }
  // }

  // @OnEvent(OrderCheckOrderStatusEvent.key, { promisify: true })
  // private async checkOrderStatusEvent(mcuid: string, order: Order) {
  //   return this.checkOrderStatus(mcuid, order);
  // }

  /** 退款 */
  async refund(dto: RefundOrderDto, user: SC.User.SysInfo) {
    // 获取订单信息
    const order = await this.repository.findOneBy({
      status: OrderResult['成功'],
      orderNo: dto.orderNo,
    });

    if (!order) {
      throw new Error('订单不存在');
    }

    const day7 =
      Dayjs(order.createdAt).add(7, 'day').valueOf() < Dayjs().valueOf();

    if (day7) {
      throw new Error('订单超过7天，无法退款');
    }

    const points = Math.abs(order.totalPrice);

    await this.pointsHistoryService.create({
      type: PointTypeEnum['退款'],
      orderNo: order.orderNo,
      points: points,
      productName: order.productName,
      productNumber: order.productNumber,
      userName: order.userName,
      userId: order.userId,
      operatorName: user.realname || '-',
      operatorId: user.uid,
      desc: `类型：${PointTypeEnum[PointTypeEnum['退款']]}，商品：${
        order.productNumber
      }，数量：${order.productName}，总价：${points}积分`,
    });

    const product = await this.productService.findOne({ id: order.productId });

    if (product) {
      await this.productService.update({
        id: product.id,
        stock: product.stock! + order.productNumber,
      });
    }

    // 更新状态
    await this.repositoryManager().update(order.id, {
      status: OrderResult['退款'],
    });
    return order;
  }
}
